home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
cpp_libs
/
tools
/
cie.lha
/
cie
/
etags++
/
etags++.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-06-21
|
13KB
|
551 lines
////////////////////////////////////////////////////////////////////////////////
// etags++.c
//
// This program reads in C++ code and generates a tags file for GNU Emacs.
// This program is more sophisticated than the standard etags program for C++
// programs. It finds all classes, structs, unions, enums, enumerators, #defines,
// typedefs, functions (both global and member), and data (both global and member).
// Furthermore, it handles C++ scoping correctly, outputting fully-scoped tags
// at the end of each line. Thus, we have modified our Emacs tags.el to search
// the fully-scoped names at the ends of the lines before the patterns. It also
// handles template syntax.
//
// In addition, we have added support for a few important macro conventions that
// we use. DECLARE_*(name,..) macros define the tag <name> in the current scope;
// DEFINE_*(class,name,...) macros define the tag <class>::<name>. We use NOTE(name)
// macros to name comments, so that you can refer to them by See::name in other
// comments. In Emacs, M-. on See::name will take you to the named comment.
//
// Note that this uses "fuzzy", quick-and-dirty parsing to find the tokens. Thus, it
// can miss some things. Also note that this is not an etags replacement -- it only
// supports C/C++ code. The etags program will still be needed for TeX, Fortran, etc.
//
// Author: Brian M. Kennedy
// (C) Copyright 1993, Intellection Inc.
// Permission is granted to use, copy, or modify this code as long as this author and
// copyright notice is maintained in all copies.
//
// Note:
// This is quick, hack code that was not written to be modifiable or maintainable -- beware!!
// I would not allow code such as this into our product! But it is okay for a quick tool hack.
// If you are a user, I hope you enjoy it. If you are modifier, my apologies ;-(
#include "c++file.c"
////////////////////////////////////////////////////////////////////////////////
struct Tag;
struct Scope
{
C_File_Pos name;
Scope* next;
Scope (const Scope& copy);
Scope (const C_File_Pos& name_arg, Scope* next_arg);
~Scope () { delete next; }
Scope* pop ();
unsigned etags_size () const;
void etags_put (ostream& os) const;
};
inline Scope* copy (const Scope* s);
Scope::
Scope (const Scope& s)
:name(s.name), next(copy(s.next))
{}
Scope::
Scope (const C_File_Pos& name_arg, Scope* next_arg)
:name(name_arg), next(copy(next_arg))
{}
inline Scope* Scope::
pop ()
{ Scope* ret = next;
next = 0;
delete this;
return ret;
}
unsigned Scope::
etags_size () const
{ if(this)
return name.length + 2 + next->etags_size();
else
return 0;
}
void Scope::
etags_put (ostream& os) const
{ if(this)
{ next->etags_put(os);
os.write(name.chars(), name.length);
os << "::";
}
}
inline Scope*
copy (const Scope* s)
{ return s ? new Scope (*s) : 0; }
////////////////////////////////////////////////////////////////////////////////
// Prefixes
char* prefix_string = " See EXPORTED set_ inc_ dec_";
File prefix_file (strlen(prefix_string), prefix_string);
C_File_Pos prefix_pos (prefix_file);
C_File_Pos see_pos ((prefix_pos.next_identifier(), prefix_pos));
C_File_Pos exported_pos ((prefix_pos.next_identifier(), prefix_pos));
C_File_Pos set_pos ((prefix_pos.next_identifier(), prefix_pos));
C_File_Pos inc_pos ((prefix_pos.next_identifier(), prefix_pos));
C_File_Pos dec_pos ((prefix_pos.next_identifier(), prefix_pos));
Scope see_scope_obj (see_pos, 0);
Scope* see_scope = &see_scope_obj;
Scope exported_scope_obj (exported_pos, 0);
Scope* exported_scope = &exported_scope_obj;
////////////////////////////////////////////////////////////////////////////////
struct Tag
{
Tag* next;
C_File_Pos name;
File_Pos pattern;
Scope* scope;
Tag (const Tag& t);
Tag (const C_File_Pos& name_arg, const Scope* scope_arg);
~Tag () { delete scope; }
unsigned etags_size () const;
void etags_put (ostream& os) const;
};
Tag::
Tag (const Tag& t)
:next(0), name(t.name), pattern(t.pattern), scope(copy(t.scope))
{}
Tag::
Tag (const C_File_Pos& name_arg, const Scope* scope_arg)
:next(0), name(name_arg), pattern(name_arg), scope(copy(scope_arg))
{ pattern.find_prev_newline();
}
unsigned Tag::
etags_size () const
{ return ((name.length + pattern.char_no - name.char_no)
+ 1 + size(name.line_no) + 1 + size(name.char_no) + 2
+ scope->etags_size() + name.length + 1);
}
void Tag::
etags_put (ostream& os) const
{ os.write(pattern.chars(), name.length + name.char_no - pattern.char_no);
os << '\177' << name.line_no << ',' << name.char_no << ",\1";
scope->etags_put(os);
os.write(name.chars(), name.length);
os << '\n';
}
////////////////////////////////////////////////////////////////////////////////
struct Tag_List
{
Tag* first;
Tag* last;
Tag_List () :first(0), last(0) {}
~Tag_List ();
void inc (Tag* tag);
unsigned etags_size () const;
void etags_put (ostream& os) const;
};
Tag_List::
~Tag_List ()
{ Tag* tag = first;
while(tag)
{ first = tag->next;
delete tag;
tag = first;
}
last = 0;
}
inline void Tag_List::
inc (Tag* tag)
{ if(last)
{ last->next = tag;
last = tag;
}
else
{ first = last = tag;
}
}
unsigned Tag_List::
etags_size () const
{ unsigned sum = 0;
for(Tag* tag = first; tag; tag = tag->next)
sum += tag->etags_size();
return sum;
}
void Tag_List::
etags_put (ostream& os) const
{ for(Tag* tag = first; tag; tag = tag->next)
tag->etags_put(os);
}
ostream&
operator << (ostream& os, Tag_List* tags)
{ tags->etags_put(os);
return os;
}
////////////////////////////////////////////////////////////////////////////////
Tag_List*
get_tags (const File& file)
{
Tag_List* tags = new Tag_List ();
Scope* scope = 0;
Scope* qualified = 0;
C_File_Pos pos (file);
C_File_Pos prev_id (pos);
while(pos.token != END_OF_FILE)
{
switch(pos.token)
{
case CLASS_KW:
case STRUCT_KW:
case UNION_KW:
pos.next_code();
if(pos.token == IDENTIFIER)
{ C_File_Pos tag_name (pos);
do
{ pos.next_code();
} while(pos.token != SEMI_COLON && pos.token != OPEN_BRACE && pos.token != END_OF_FILE);
if(pos.token == OPEN_BRACE)
{ tags->inc(new Tag(tag_name, scope));
scope = new Scope(tag_name, scope);
}
}
else
{ while(pos.token != SEMI_COLON && pos.token != OPEN_BRACE && pos.token != END_OF_FILE)
pos.next_code();
if(pos.token == OPEN_BRACE)
pos.close_brace();
}
break;
case ENUM_KW:
pos.next_code();
if(pos.token == IDENTIFIER)
{ C_File_Pos tag_name (pos);
do
{ pos.next_code();
} while(pos.token != SEMI_COLON && pos.token != OPEN_BRACE && pos.token != END_OF_FILE);
if(pos.token == OPEN_BRACE)
tags->inc(new Tag (tag_name, scope));
}
if(pos.token == OPEN_BRACE)
{ while(pos.token != CLOSE_BRACE && pos.token != END_OF_FILE)
{ pos.next_code();
if(pos.token == IDENTIFIER)
tags->inc(new Tag (pos, scope));
do
{ pos.next_code();
} while(pos.token != COMMA && pos.token != CLOSE_BRACE && pos.token != END_OF_FILE);
}
}
while(pos.token != SEMI_COLON && pos.token != END_OF_FILE)
pos.next_code();
break;
case TYPEDEF_KW: // only catches last typedef (e.g 'c' in typedef int a, b, c;)
{ C_File_Pos next (pos);
next.next_code();
do
{ pos = next;
next.next_code();
} while(next.token != SEMI_COLON && next.token != OPEN_PARE && next.token != END_OF_FILE);
if(next.token == OPEN_PARE && next.chars(1) == '*')
{ // Function Typedef
next.next_code();
if(next.token == IDENTIFIER)
tags->inc(new Tag (next, scope));
}
while(next.token != SEMI_COLON && next.token != END_OF_FILE)
{ pos = next;
next.next_code();
}
if(pos.token == IDENTIFIER)
tags->inc(new Tag (pos, scope));
pos = next;
}
break;
case DEFINE:
pos.next_code();
if(pos.token == IDENTIFIER || pos.token == DECLARE_MACRO || pos.token == DEFINE_MACRO)
tags->inc(new Tag (pos, scope));
pos.close_define();
break;
case CLOSE_BRACE:
if(scope) scope = scope->pop();
break;
case OPEN_BRACE:
pos.close_brace();
break;
case OPEN_PARE:
if(prev_id.token == IDENTIFIER)
{ tags->inc(new Tag(prev_id, qualified));
prev_id = pos;
}
pos.close_pare();
pos.close_func();
break;
case COLONS:
if(prev_id.token == IDENTIFIER)
{ qualified = new Scope(prev_id, qualified);
prev_id = pos;
}
break;
case IDENTIFIER:
if(prev_id.token != COLONS)
{ delete qualified;
qualified = copy(scope);
}
prev_id = pos;
break;
case SEMI_COLON:
if(prev_id.token == IDENTIFIER)
{ tags->inc(new Tag(prev_id, qualified));
prev_id = pos;
}
break;
case EQUAL:
if(prev_id.token == IDENTIFIER)
{ tags->inc(new Tag(prev_id, qualified));
prev_id = pos;
}
pos.close_func();
break;
case NOTE:
pos.next_code();
if(pos.token == OPEN_PARE)
{ pos.next_code();
if(pos.token == IDENTIFIER)
tags->inc(new Tag(pos, see_scope));
if(pos.token != CLOSE_PARE)
pos.close_pare();
}
while(pos.token != SEMI_COLON && pos.token != END_OF_FILE)
pos.next_code();
break;
case EXPORT:
pos.next_code();
if(pos.token == OPEN_PARE)
{ do
{ pos.next_code();
} while (pos.token != COMMA && pos.token != END_OF_FILE);
pos.next_code();
if(pos.token == IDENTIFIER)
tags->inc(new Tag(pos, exported_scope));
if(pos.token != CLOSE_PARE)
pos.close_pare();
}
while(pos.token != SEMI_COLON && pos.token != END_OF_FILE)
pos.next_code();
break;
case DECLARE_MACRO:
pos.next_code();
if(pos.token == OPEN_PARE)
{ pos.next_code();
if(pos.token == IDENTIFIER)
tags->inc(new Tag(pos, scope));
if(pos.token != CLOSE_PARE)
pos.close_pare();
}
while(pos.token != SEMI_COLON && pos.token != END_OF_FILE)
pos.next_code();
break;
case DEFINE_MACRO:
pos.next_code();
if(pos.token == OPEN_PARE)
{ pos.next_code();
if(pos.token == IDENTIFIER)
{ qualified = new Scope (pos, 0);
while(pos.token != COMMA && pos.token != CLOSE_PARE && pos.token != END_OF_FILE)
pos.next_code();
if(pos.token == COMMA)
{ pos.next_code();
if(pos.token == IDENTIFIER)
tags->inc(new Tag(pos, qualified));
}
qualified = qualified->pop();
}
if(pos.token != CLOSE_PARE)
pos.close_pare();
}
break;
case DEFINE_GET:
case DEFINE_GETSET:
pos.next_code();
if(pos.token == OPEN_PARE)
{ while(pos.token != COMMA && pos.token != CLOSE_PARE && pos.token != END_OF_FILE)
pos.next_code();
if(pos.token == COMMA)
{ pos.next_code();
if(pos.token == IDENTIFIER)
tags->inc(new Tag(pos, scope));
}
if(pos.token != CLOSE_PARE)
pos.close_pare();
}
break;
case DEFINE_SET:
case DEFINE_INC:
case DEFINE_DEC:
pos.next_code();
if(pos.token == OPEN_PARE)
pos.close_pare();
break;
default:
;
}
pos.next_code();
}
return tags;
}
////////////////////////////////////////////////////////////////////////////////
void
usage_error (const char* progname)
{ cerr << "Usage " << progname << " [-a] [-f outfile] [-k max_infile_kbytes] infile ..."
<< endl;
exit(BAD);
}
main (int argc, char** argv)
{
unsigned argi = 0;
char* progname = argv[argi];
// Default flags
Boolean append = FALSE;
const char* outfile = 0;
int size = 1024;
// Process flags
for(argi = 1; argi < argc && argv[argi][0] == '-'; ++argi)
{
Boolean done = FALSE;
for(unsigned chari = 1; !done && argv[argi][chari]; ++chari)
{
switch(argv[argi][chari])
{
case '?':
case 'h':
usage_error(progname);
case 'a':
append = TRUE;
break;
case 'f':
if(outfile)
{ cerr << "The -f option may only be given once." << endl;
usage_error(progname);
}
if(argv[argi][chari+1])
{ done = TRUE;
outfile = &argv[argi][chari+1];
}
else if(argi < argc)
{ done = TRUE;
outfile = argv[++argi];
}
else
{ cerr << "The -f option must be given an argument (the outfile name)" << endl;
usage_error(progname);
}
break;
case 'k':
if(argv[argi][chari+1])
{ done = TRUE;
size = atoi(&argv[argi][chari+1]);
}
else if(argi < argc)
{ done = TRUE;
size = atoi(argv[++argi]);
}
else
{ cerr << "The -k option must be given an argument (the max_file_size in kbytes)" << endl;
usage_error(progname);
}
break;
default:
;
}
}
}
// Arg value checks
if(size < 64)
size = 64;
if(!outfile)
outfile = "TAGS";
// Create output TAGS file
ofstream out (outfile, (append ? ios::app : ios::out));
// Create File for input
File infile (size*1024);
// Process files
for(; argi < argc; ++argi)
{
infile.read(argv[argi]);
Tag_List* tags = get_tags(infile);
out << "\f\n" << argv[argi] << ',' << tags->etags_size() << '\n' << tags;
delete tags;
}
out << flush;
exit(GOOD);
}